package dev.ideas.funkyj.function;

import dev.ideas.funkyj.support.PredicateSupport;

import java.io.Serializable;
import java.util.Collection;

import static dev.ideas.funkyj.support.PredicateSupport.*;

/**
 */
//todo remove this class if possible
public abstract class _Function<X, Y> implements Serializable {
    private String string;

    @Deprecated /*better use a named function*/ protected _Function() { this.string = super.toString(); }
    protected _Function(String string) { this.string = string; }

    public abstract Y apply(X x);

    public <X1> F<X1, Y> of(final F<X1, X> func) {
        return new F<X1, Y>() {
            public Y apply(X1 x1) { return _Function.this.apply(func.apply(x1)); }
        };
    }
    public Y of(X x) { return apply(x); }

    public <T1> F<X, T1> on(final F<? super Y, T1> func) {
        return new F<X, T1>(func + "(" + _Function.this + ")") {
            public T1 apply(X x) {
                return func.apply(_Function.this.apply(x));
            }
        };
    }

    public <T1> F<X, T1> dot(final F<Y, T1> func) {
        return on(func);
    }

    public <Y1> F<X, Y1> than(final F<Y, Y1> func) {
        return dot(func);
    }

    public P<X> is(final Y y) {
        return new P<X>() {
            public Boolean apply(X x) {
                return _Function.this.apply(x).equals(y);
            }
        };
    }

    public P<X> isNot(final Y y) {
        return not(is(y));
    }

    public P<X> is(final P<? super Y> t) {
        return new P<X>() {
            public Boolean apply(X x) {
                return t.apply(_Function.this.apply(x));
            }
        };
    }

    public P<X> isNot(final P<? super Y> t) {
        return not(is(t));
    }

    public P<X> in(final Y... ys) {
        return is(PredicateSupport.in(ys));
    }
    public P<X> in(final Collection<Y> ys) {
        return is(PredicateSupport.in(ys));
    }

    //todo how to implement this?
//    public <T1> Function2<F, T, T1> with(final Function<F, T1> getPrice) {
//        return new Function2<F, T, T1>() {
//            @Override public T1 apply(F o, T o1) {
//                return null;
//            }
//        };
//    }

    @Override public String toString() { return string; }

}
